/*!
\copyright  Copyright (c) 2017 Qualcomm Technologies International, Ltd.
            All Rights Reserved.
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\version    Part of 6.3.0
\file       adxl362.c
\brief      Support for the adxl362 accelerometer
*/

#ifdef INCLUDE_ACCELEROMETER
#ifdef HAVE_ADXL362
#include <bitserial_api.h>
#include <panic.h>
#include <pio.h>
#include <hydra_macros.h>
#include <input_event_manager.h>

#include "../av_headset.h"
#include "../av_headset_log.h"
#include "../av_headset_accelerometer.h"
#include "adxl362.h"

/*! \brief Returns the PIOs bank number.
    \param pio The pio.
*/
#define PIO2BANK(pio) ((uint16)((pio) / 32))
/*! \brief Returns the PIO bit position mask within a bank.
    \param pio The pio.
*/
#define PIO2MASK(pio) (1UL << ((pio) % 32))

/*! \brief Read a register from the accelerometer */
static bool DA230ReadRegister(bitserial_handle handle, uint8 reg,  uint8 *value)
{
    bitserial_result result;

    /* Write the command and register to read */
    result = BitserialWrite(handle, BITSERIAL_NO_MSG,
                            &reg, 1, BITSERIAL_FLAG_BLOCK);
    if (result == BITSERIAL_RESULT_SUCCESS)
    {
        /* Read the register value */
        result = BitserialRead(handle, BITSERIAL_NO_MSG,
                               value, 1, BITSERIAL_FLAG_BLOCK);
    }
    return (result == BITSERIAL_RESULT_SUCCESS);
}

/*! \brief Write a single accelerometer register */
static bool DA230WriteRegister(bitserial_handle handle, uint8 reg, uint8 value)
{
    uint8 command[2] = {reg, value};
    return (BitserialWrite(handle, BITSERIAL_NO_MSG,
                           command, 2,
                           BITSERIAL_FLAG_BLOCK) == BITSERIAL_RESULT_SUCCESS);
}

static bool mir3da_register_mask_write(bitserial_handle handle, uint8 reg, uint8 mask, uint8 value){
	bool res;
	uint8 temp_data;
	res = DA230ReadRegister(handle, reg, &temp_data);
	if(!res) {
		return res;
	}
	temp_data &= ~mask;
	temp_data |= value & mask;
	res = DA230WriteRegister(handle, reg, temp_data);
	return res;
}

/*! \brief Read the proximity sensor version */
static bool adxl362ReadVersion(bitserial_handle handle, uint8 *reg)
{
	DEBUG_LOG("adxl362ReadVersion");
    return DA230ReadRegister(handle, NSA_REG_WHO_AM_I, reg);
}

static bool mir3da_init(bitserial_handle handle){
	bool res;
	uint8 data=0;
	DEBUG_LOG("mir3da_init");

	res = DA230ReadRegister(handle, NSA_REG_WHO_AM_I,&data);
	if(data != 0x13){
		return FALSE;
	}
	res |= mir3da_register_mask_write(handle, NSA_REG_G_RANGE, 0xFF, 0x40);

	res |= mir3da_register_mask_write(handle, NSA_REG_POWERMODE_BW, 0xFF, 0x3E);
	res |= mir3da_register_mask_write(handle, NSA_REG_ODR_AXIS_DISABLE, 0xFF, 0x07);
	
	/*res |= mir3da_open_step_counter();*/

	return res;	    	
}

/*power control,disable mean sleep */
static bool mir3da_set_enable(bitserial_handle handle, char enable)
{
	bool res;
	if(enable)
	res = mir3da_register_mask_write(handle, NSA_REG_POWERMODE_BW,0xC0,0x40);
	else	
	res = mir3da_register_mask_write(handle, NSA_REG_POWERMODE_BW,0xC0,0x80);
	
	return res;	
}

#if 0
static bool mir3da_open_interrupt(bitserial_handle handle, uint8 num){
	bool res;

	res |= mir3da_register_mask_write(handle, NSA_REG_INTERRUPT_SETTINGS1, 0x87, 0x87);
	res = DA230WriteRegister(handle, NSA_REG_ACTIVE_THRESHOLD,0x05 );/*0x05~0xff,0x1f*/
			
	switch(num){
		case 0:
			res |= mir3da_register_mask_write(handle, NSA_REG_INTERRUPT_MAPPING1, 0x04, 0x04);
			break;

		case 1:
			res |= mir3da_register_mask_write(handle, NSA_REG_INTERRUPT_MAPPING3, 0x04, 0x04);
			break;
	}

	return res;
}

static bool mir3da_close_interrupt(bitserial_handle handle, uint8 num){
	bool res;

	res |= mir3da_register_mask_write(handle, NSA_REG_INTERRUPT_SETTINGS1, 0x87, 0x00);
			
	switch(num){
		case 0:
			res |= mir3da_register_mask_write(handle, NSA_REG_INTERRUPT_MAPPING1, 0x04, 0x00);
			break;

		case 1:
			res |= mir3da_register_mask_write(handle, NSA_REG_INTERRUPT_MAPPING3, 0x04, 0x00);
			break;
	}

	return res;
}
#endif

static bool mir3da_open_d_tap_interrupt(bitserial_handle handle){
	bool res = 0;

	res |= DA230WriteRegister(handle, NSA_REG_INT_LATCH,0X0D);
	res |= DA230WriteRegister(handle, NSA_REG_TAP_DURATION,0X45);
	res |= DA230WriteRegister(handle, NSA_REG_TAP_THRESHOLD,0X0D);/*rang:0x05-0xff*/	
	res |= mir3da_register_mask_write(handle, NSA_REG_INTERRUPT_MAPPING1, 0x10, 0x10);
	res |= mir3da_register_mask_write(handle, NSA_REG_INTERRUPT_SETTINGS1, 0x10, 0x10);

	return res;
}

static bool mir3da_close_d_tap_interrupt(bitserial_handle handle){
    bool res = 0;
	
	res |= mir3da_register_mask_write(handle, NSA_REG_INTERRUPT_MAPPING1, 0x10, 0x00);
	res |= mir3da_register_mask_write(handle, NSA_REG_INTERRUPT_SETTINGS1, 0x10, 0x00);

	return res;
}


/*! Get the client message based on the PIO state and mask.
    The sensor signals detected motion by setting the masked PIO high. */
static MessageId getMessage(uint32 pio_state, uint32 pio_mask)
{
    bool in_motion = (0 != (pio_state & pio_mask));
    return in_motion ? ACCELEROMETER_MESSAGE_IN_MOTION : ACCELEROMETER_MESSAGE_NOT_IN_MOTION;
}

/*! \brief Handle the accelerometer interrupt */
static void adxl362InterruptHandler(Task task, MessageId id, Message msg)
{
    accelTaskData *accel = (accelTaskData *)task;
    UNUSED(accel);
    switch(id)
    {
        case MESSAGE_PIO_CHANGED:
        {
            const MessagePioChanged *mpc = (const MessagePioChanged *)msg;
            uint32 state = mpc->state16to31 << 16 | mpc->state;
            uint16 bank = PIO2BANK(accel->config->pios.interrupt);
            uint32 mask = PIO2MASK(accel->config->pios.interrupt);
            if (mpc->bank == bank)
            {
		  DEBUG_LOG("MESSAGE_PIO_CHANGED");
                appTaskListMessageSendId(accel->clients, getMessage(state, mask));
            }
        }
        break;
        default:
        break;
    }
}

/*! \brief Enable the accelerometer */
static bitserial_handle adxl362Enable(const accelerometerConfig *config)
{
    bitserial_config bsconfig;
    uint32 i;
    uint16 bank;
    uint32 mask;
    struct
    {
        uint16 pio;
        pin_function_id func;
    } i2c_pios[] = {{config->pios.i2c_scl, BITSERIAL_1_CLOCK_OUT},
                    {config->pios.i2c_scl, BITSERIAL_1_CLOCK_IN},
                    {config->pios.i2c_sda, BITSERIAL_1_DATA_OUT},
                    {config->pios.i2c_sda, BITSERIAL_1_DATA_IN}};

    DEBUG_LOG("adxl362Enable");

    if (config->pios.on != ADXL362_ON_PIO_UNUSED)
    {
        /* Enable PIO to power the accelerometer as an output */
        bank = PIO2BANK(config->pios.on);
        mask = PIO2MASK(config->pios.on);
        PanicNotZero(PioSetMapPins32Bank(bank, mask, mask));
        PanicNotZero(PioSetDir32Bank(bank, mask, mask));
        PanicNotZero(PioSet32Bank(bank, mask, mask));
    }

    for (i = 0; i < ARRAY_DIM(i2c_pios); i++)
    {
        uint16 pio = i2c_pios[i].pio;
        bank = PIO2BANK(pio);
        mask = PIO2MASK(pio);

        /* Setup I2C PIOs with strong pull-up */
        PanicNotZero(PioSetMapPins32Bank(bank, mask, 0));
        PanicFalse(PioSetFunction(pio, i2c_pios[i].func));
        PanicNotZero(PioSetDir32Bank(bank, mask, 0));
        PanicNotZero(PioSet32Bank(bank, mask, mask));
        PanicNotZero(PioSetStrongBias32Bank(bank, mask, mask));
    }

    /* Setup Interrupt as input with weak pull up */
    bank = PIO2BANK(config->pios.interrupt);
    mask = PIO2MASK(config->pios.interrupt);
    PanicNotZero(PioSetMapPins32Bank(bank, mask, mask));
    PanicNotZero(PioSetDir32Bank(bank, mask, 0));
    PanicNotZero(PioSet32Bank(bank, mask, mask));

    /* Configure Bitserial to work with adxl362 accelerometer */
    memset(&bsconfig, 0, sizeof(bsconfig));
    bsconfig.mode = BITSERIAL_MODE_I2C_MASTER;
    bsconfig.clock_frequency_khz = config->i2c_clock_khz;
    bsconfig.u.i2c_cfg.i2c_address = I2C_ADDRESS;
    return BitserialOpen((bitserial_block_index)BITSERIAL_BLOCK_1, &bsconfig);
}

/*! \brief Disable the accelerometer */
static void adxl362Disable(bitserial_handle handle, const accelerometerConfig *config)
{
    uint16 bank;
    uint32 mask;
    DEBUG_LOG("adxl362Disable");

    /* Disable interrupt and set weak pull down */
    bank = PIO2BANK(config->pios.interrupt);
    mask = PIO2MASK(config->pios.interrupt);
    PanicNotZero(PioSet32Bank(bank, mask, 0));

    /* Release bitserial instance */
    BitserialClose(handle);
    handle = BITSERIAL_HANDLE_ERROR;

    if (config->pios.on != ADXL362_ON_PIO_UNUSED)
    {
        /* Power off the Accelerometer*/
        bank = PIO2BANK(config->pios.on);
        mask = PIO2MASK(config->pios.on);
        PanicNotZero(PioSet32Bank(bank, mask, 0));
    }
}

bool appAccelerometerClientRegister(Task task)
{
    accelTaskData *accel = appGetAccelerometer();

    if (NULL == accel->clients)
    {
        uint8 revision;

        accel->clients = appTaskListInit();
        accel->config = appConfigAccelerometer();

        accel->handle = adxl362Enable(accel->config);
        PanicFalse(accel->handle != BITSERIAL_HANDLE_ERROR);
	DEBUG_LOG("adxl362Enable111");

        PanicFalse(adxl362ReadVersion(accel->handle, &revision));
        DEBUG_LOGF("G_sensor ID=0x%x", revision);

        PanicFalse(mir3da_init(accel->handle));

        /* Register for interrupt events */
        accel->task.handler = adxl362InterruptHandler;
        InputEventManagerRegisterTask(&accel->task, accel->config->pios.interrupt);

        PanicFalse(mir3da_open_d_tap_interrupt(accel->handle));
        PanicFalse(mir3da_set_enable(accel->handle, TRUE));
    }

    /* Send a state message to the registering client: read the interrupt PIO state */
    uint16 bank = PIO2BANK(accel->config->pios.interrupt);
    uint32 mask = PIO2MASK(accel->config->pios.interrupt);
    MessageSend(task, getMessage(PioGet32Bank(bank), mask), NULL);

    return appTaskListAddTask(accel->clients, task);
}

void appAccelerometerClientUnregister(Task task)
{
    accelTaskData *accel = appGetAccelerometer();

    appTaskListRemoveTask(accel->clients, task);
    if (0 == appTaskListSize(accel->clients))
    {
        appTaskListDestroy(accel->clients);
        accel->clients = NULL;

        PanicFalse(accel->handle != BITSERIAL_HANDLE_ERROR);

        /* Unregister for interrupt events */
        InputEventManagerUnregisterTask(&accel->task, accel->config->pios.interrupt);
		
        PanicFalse(mir3da_close_d_tap_interrupt(accel->handle));

        adxl362Disable(accel->handle, accel->config);
        accel->handle = BITSERIAL_HANDLE_ERROR;
    }
}

bool appAccelerometerGetDormantConfigureKeyValue(dormant_config_key *key, uint32* value)
{
    accelTaskData *accel = appGetAccelerometer();
    uint8 interrupt = accel->config->pios.interrupt;

    if (appConfigPioIsLed(interrupt))
    {
        *key = LED_WAKE_MASK;
        *value = 1 << appConfigPioLedNumber(interrupt);
    }
    else if (appConfigPioCanWakeFromDormant(interrupt))
    {
        *key = PIO_WAKE_MASK;
        *value = 1 << interrupt;
    }
    else
    {
        DEBUG_LOGF("The accelerometer interrupt PIO (%d) cannot wake the chip from dormant", interrupt);
        return FALSE;
    }
    return TRUE;
}

#endif /* HAVE_ADXL362 */
#endif /* INCLUDE_ACCELEROMETER */
